package com.lumlord.common.spring;

import com.google.gson.*;
import com.lumlord.common.annotation.Api;
import com.lumlord.common.base.ApiController;
import com.lumlord.common.domain.User;
import com.lumlord.common.exception.BusinessException;
import com.lumlord.common.shiro.config.JwtAuthFilter;
import com.lumlord.common.shiro.util.ShiroUtils;
import com.lumlord.model.custom.Page;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;

import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;

/**
 * 多RequestBody解析器
 *
 * @author GPF
 * @date 2019/09/09
 */
@Component
public class RequestJsonBody implements HandlerMethodArgumentResolver {



	public static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";

	/**
	 * 是否json解析
	 */

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Api api = parameter.getExecutable().getAnnotation(Api.class);
		if (null != api) {
			//return api.isJsonRequest() == true;
			return true;
		}
		return false;
	}

	private boolean isJsonRequest(HttpServletRequest request) {
		Object allowJsonRequest = request.getAttribute(JwtAuthFilter.LUMLORD_REQUEST_JSON);
		if(null!=allowJsonRequest){
			return true;
		}

		String requestAccept = request.getHeader("accept");
		return requestAccept != null && "application/json".equals(requestAccept);
	}

	private boolean isSingle(Class clazz) {
		if (clazz.equals(java.lang.String.class)) {

		} else if (clazz.equals(java.lang.Integer.class)) {

		} else if (clazz.equals(java.lang.Double.class)) {

		} else if (clazz.equals(java.lang.Long.class)) {

		} else if (clazz.equals(java.lang.Byte.class)) {

		}else {
			return false;
		}


		return true;
	}


	/**
	 * 参数解析，利用fastjson 注意：非基本类型返回null会报空指针异常，要通过反射或者JSON工具类创建一个空对象
	 */
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		if (!isJsonRequest(request)) {
			//throw new BusinessException("不是json请求");
		}
		
		//设置来源
		if(null ==request.getAttribute(ApiController.lumlordVisit)) {
			request.setAttribute(ApiController.lumlordVisit,resolvPathArgs(webRequest, ApiController.lumlordVisit));
		}
		
		String jsonBody = getRequestBody(webRequest);
		String name = parameter.getParameterName();
		// 兼容 swigger form 请求


		Object resolvCurrentUser = resolvCurrentUser(parameter, name);
		if (null != resolvCurrentUser) {
			return resolvCurrentUser;
		}

		Object val = resolvPathArgs(webRequest, name);
		if (val != null) {
			return Convert.convert(parameter.getParameterType(), val);
		}

		if (jsonBody.length() == 0) {

			if(parameter.getParameterType().isPrimitive()||isSingle(parameter.getParameterType())) {

				return Convert.convert(parameter.getParameterType(), request.getParameter(name));
			}

			Object o = new RequestParamMapMethodArgumentResolver().resolveArgument(parameter, mavContainer, webRequest,
					binderFactory);
			Map map = (Map) o;
			return BeanUtil.mapToBean(map, parameter.getParameterType(), false);
		}



		JsonObject jsonObject = (JsonObject) new JsonParser().parse(jsonBody);
		JsonElement ele = jsonObject.get(name);
		Gson gson = new GsonBuilder()
				.setDateFormat("yyyy-MM-dd HH:mm:ss")
				.create();

		Object jsonVal =  gson.fromJson(ele, parameter.getParameterType());

		if(null == jsonVal  ) {
			if(parameter.getParameterType().equals(Page.class)) {
				Page page = new Page();
				page.setPageNum(1); page.setPageSize(10);
				return page;
			}

		}
		return jsonVal;

	}

	/**
	 * 解析路径参数
	 *
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Object resolvPathArgs(NativeWebRequest webRequest, String name) {
		Map<String, String> uriTemplateVars = (Map<String, String>) webRequest
				.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
		Object val = (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
		return val;
	}

	/**
	 * 注入用户
	 *
	 * @return
	 */
	private Object resolvCurrentUser(MethodParameter parameter, String name) {
		Api api = parameter.getExecutable().getAnnotation(Api.class);
		if (null != api && api.feachCurrentUser() && parameter.getParameterType().equals(User.class)) {
			return ShiroUtils.getUser();
		} else {
			return null;
		}
	}



	/**
	 * 获取请求体JSON字符串
	 */
	private String getRequestBody(NativeWebRequest webRequest) {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);

		// 有就直接获取
		String jsonBody = (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
		// 没有就从请求中读取
		if (jsonBody == null) {
			try {
				jsonBody = IOUtils.toString(servletRequest.getReader());
				webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
			} catch (Exception e) {
				//throw new RuntimeException(e);
				webRequest.setAttribute("JSON_REQUEST_BODY", "{}", 0);
				return "{}";
			}
		}
		return jsonBody;
	}
	
	
	 
	/**
	 * 获取访问端 app true 
	 * @param webRequest
	 * @return
	 */
	public boolean isApp(HttpServletRequest request) {
		Object o = request.getAttribute(ApiController.lumlordVisit);
		if(null==o) {
			if(request.getRequestURI().toLowerCase().indexOf("app")!=-1) {
				return true;
			}else {
				return false;
			}
		}
		if((""+o).toLowerCase().equals("app")) {
			return true;
		}else {
			return false;
		}
	}
	
	
	/**
	 * 获取访问端 app true 
	 * @param webRequest
	 * @return
	 */
	public boolean isApp() {
		HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
		return isApp(request);
	}
	
	/**
	 * 获取请求体JSON字符串
	 */
	public String getRequestBody(HttpServletRequest servletRequest ) {

		// 有就直接获取
		String jsonBody = (String) servletRequest.getAttribute(JSONBODY_ATTRIBUTE);
		// 没有就从请求中读取
		if (jsonBody == null) {
			try {
				jsonBody = IOUtils.toString(servletRequest.getReader());
				servletRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonBody);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return jsonBody;
	}

}